#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <time.h>
#include <getopt.h>
#include <stdlib.h>


/*
venus:/sys/kernel/tracing # cat set_event
sched:sched_kthread_stop
sched:sched_kthread_stop_ret
sched:sched_enq_deq_task
sched:sched_waking
sched:sched_wakeup
sched:sched_wakeup_new
sched:sched_switch
sched:sched_migrate_task
sched:sched_load_balance
sched:sched_load_balance_nohz_kick
sched:sched_load_balance_sg_stats
sched:sched_load_balance_stats
sched:sched_process_free
sched:sched_process_exit
sched:sched_wait_task
sched:sched_process_wait
sched:sched_process_fork
sched:sched_process_exec
sched:sched_stat_wait
sched:sched_stat_sleep
sched:sched_stat_iowait
sched:sched_stat_blocked
sched:sched_blocked_reason
sched:sched_stat_runtime
sched:sched_pi_setprio
sched:sched_move_numa
sched:sched_stick_numa
sched:sched_swap_numa
sched:sched_wake_idle_without_ipi
sched:pelt_task_se_load
sched:sched_load_balance_skip_tasks
sched:sched_cpu_util
sched:sched_compute_energy
sched:sched_task_util
sched:sched_find_best_target
sched:sched_overutilized
sched:sched_update_pred_demand
sched:sched_update_history
sched:sched_get_task_cpu_cycles
sched:sched_update_task_ravg
sched:sched_update_task_ravg_mini
sched:sched_set_preferred_cluster
sched:sched_migration_update_sum
sched:sched_set_boost
sched:sched_load_to_gov
sched:core_ctl_eval_need
sched:core_ctl_set_busy
sched:core_ctl_set_boost
sched:core_ctl_update_nr_need
sched:core_ctl_notif_data
sched:sched_get_nr_running_avg
sched:sched_isolate
sched:sched_ravg_window_change
*/
static char *trace_events[] = {
        "power:sugov_next_freq",
        "power:sugov_util_update",
        // "sched:sched_switch",
        // "sched:sched_wakeup",
        // "sched:sched_migrate_task",
        "sched:*",
        "irq:*",
};

#define NEED_SET_PID            0
#define MAX_DOWNMIGRATE_LATENCY_US 100000
#define MAX_INCORRECT_CLUSTER_PCT 10
#define BURN_SEC 5
#define USEC_PER_SEC (1000 * 1000)

#define SET_EVENT_FILE          "/sys/kernel/tracing/set_event"
#define SET_EVENT_PID_FILE      "/sys/kernel/tracing/set_event_pid"
#define TRACE_MARKER_FILE      "/sys/kernel/tracing/trace_makrer"

#define SAVING_TRACE_FILE "/data/local/tmp/test_trace"

enum test_item_t {
        BIG_TO_SMALL = 1,
        SMALL_TO_BIG
};

enum lock_freq_t {
        LOCK_TO_HIGH = 1,
        LOCK_TO_LOW
};


static char *save_trace  = "cat /sys/kernel/tracing/trace > /data/local/tmp/test_trace";

void burn(unsigned int sec, int sleep)
{
	unsigned long long now_usec, end_usec;
        unsigned int usec = sec * USEC_PER_SEC;
	struct timespec ts;

	if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
		printf("clock_gettime() reported an error\n");
		return;
	}
	end_usec = (ts.tv_sec) * USEC_PER_SEC + (ts.tv_nsec / 1000) + usec;
	while(1) {
		if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
			printf("clock_gettime() reported an error\n");
			return;
		}
		now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000;
		if (now_usec > end_usec)
			return;
		if (sleep)
			usleep(1000);
	}
}

int tracefs_write(char *file, char *value)
{
        char file_name[256] = {0};

        snprintf(file_name, sizeof(file_name), "/sys/kernel/tracing/%s", file);
        int fd = open(file_name, O_WRONLY|O_CREAT|O_TRUNC, 0666);
        if (fd > 0)
                return write(fd, value, strlen(value));
        else
                printf("open %s error, fd = %d", file, fd);
}

void start_trace(void)
{
        int trace_num = sizeof(trace_events) / sizeof(trace_events[0]);
        char cmd[256] = {0};
	int task_tid = gettid();

        tracefs_write("tracing_on", "0");
        tracefs_write("set_event", "0");
        tracefs_write("trace", "0");

        for (int i = 0; i < trace_num; i++) {
                memset(cmd, 0, sizeof(cmd));
                snprintf(cmd, sizeof(cmd), "echo %s >> %s", trace_events[i], SET_EVENT_FILE);
                system(cmd);
        }

// #ifdef NEED_SET_PID
//         // set trace pid
//         char *pid_str;
//         sprintf(pid_str, "%d", task_tid);
//         tracefs_write("set_event_pid", pid_str);
// #endif

        // tracing_on
        tracefs_write("tracing_on", "1");

        return;
}

void stop_trace(void)
{
        tracefs_write("tracing_on", "0");
        system(save_trace);

        return;
}

void save_and_set_freq(void)
{
        return;
}

void restore_freq(void)
{
        return;
}


static void eas_big_to_small_fn(void)
{
        int burn_sec = BURN_SEC;
	int i =0;

	printf("Big task executing for %ds...\n", BURN_SEC);
	tracefs_write("trace_marker", "BIG_TASK_START");
	burn(burn_sec, 0);
	tracefs_write("trace_marker", "BIG_TASK_END");

	printf("Changing to small task...\n");
	tracefs_write("trace_marker", "SMALL_TASK_START");
	burn(burn_sec, 1);
	tracefs_write("trace_marker", "SMALL_TASK_END");

	return;
}

static void eas_small_to_big_fn(void)
{
        int burn_sec = BURN_SEC;
	int i =0;

	printf("Small task executing for %ds...\n", BURN_SEC);
	tracefs_write("trace_marker", "SMALL_TASK_START");
	burn(burn_sec, 1);
	tracefs_write("trace_marker", "SMALL_TASK_END");

	printf("Changing to big task...\n");
	tracefs_write("trace_marker", "BIG_TASK_START");
	burn(burn_sec, 0);
	tracefs_write("trace_marker", "BIG_TASK_END");
}



int main(int argc, char *argv[])
{
        char opt;
        char *string = "r:f:m";
        enum test_item_t test = 1;
        enum lock_freq_t freq = 1;
        int i = 0;
        printf("i am suhui\n");
        while ((opt = getopt(argc, argv, string)), i < 2) {
                i++;
                switch (opt) {
                        case 'r':
                                if (NULL != strstr("eas_big_to_small", optarg)) {
                                        test = BIG_TO_SMALL;
                                } else if (NULL != strstr("eas_small_to_big", optarg)) {
                                        test = SMALL_TO_BIG;
                                }
                                break;
                        case 'f':
                                if (NULL != strstr("low", optarg)) {
                                        freq = LOCK_TO_LOW;
                                } else if (NULL != strstr("high", optarg)) {
                                        freq = LOCK_TO_HIGH;
                                }
                                break;
                }
        }

        printf("test is %s, freq is %s\n", test == 1? "big_to_small": "small_to_big", freq == 1? "low": "high");

        save_and_set_freq();
        start_trace();
        switch (test) {
                case (BIG_TO_SMALL):
                        eas_big_to_small_fn();
                        break;
                case (SMALL_TO_BIG):
                        eas_small_to_big_fn();
                        break;
        }

        stop_trace();
        restore_freq();

        return 0;
}